1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 import java.awt.*;
33 import java.awt.geom.*;
34 import java.awt.image.*;
35 import java.awt.event.*;
36 import java.util.Vector;
37 import java.io.*;
38
39 public class RenderClipTest {
40 public static double randDblCoord() {
41 return Math.random()*60 - 10;
42 }
43
44 public static float randFltCoord() {
45 return (float) randDblCoord();
46 }
47
48 public static int randIntCoord() {
49 return (int) Math.round(randDblCoord());
50 }
51
52 public static int randInt(int n) {
53 return ((int) (Math.random() * (n*4))) >> 2;
54 }
55
56 static int numtests;
57 static int numerrors;
58 static int numfillfailures;
59 static int numstrokefailures;
60 static int maxerr;
61
62 static boolean useAA;
63 static boolean strokePure;
64 static boolean testFill;
65 static boolean testDraw;
66 static boolean silent;
67 static boolean verbose;
68 static boolean strict;
69 static boolean showErrors;
70 static float lw;
71 static double rot;
72
73 static BufferedImage imgref;
74 static BufferedImage imgtst;
75
76 static Graphics2D grefclear;
77 static Graphics2D gtstclear;
78 static Graphics2D grefrender;
79 static Graphics2D gtstrender;
80
81 public static abstract class AnnotatedRenderOp {
82 public static AnnotatedRenderOp parse(String str) {
83 AnnotatedRenderOp ar;
84 if (((ar = Cubic.tryparse(str)) != null) ||
85 ((ar = Quad.tryparse(str)) != null) ||
86 ((ar = Poly.tryparse(str)) != null) ||
87 ((ar = Path.tryparse(str)) != null) ||
88 ((ar = Rect.tryparse(str)) != null) ||
89 ((ar = Line.tryparse(str)) != null) ||
90 ((ar = RectMethod.tryparse(str)) != null) ||
91 ((ar = LineMethod.tryparse(str)) != null))
92 {
93 return ar;
94 }
95 System.err.println("Unable to parse shape: "+str);
96 return null;
97 }
98
99 public abstract void randomize();
100
101 public abstract void fill(Graphics2D g2d);
102
103 public abstract void draw(Graphics2D g2d);
104 }
105
106 public static abstract class AnnotatedShapeOp extends AnnotatedRenderOp {
107 public abstract Shape getShape();
108
109 public void fill(Graphics2D g2d) {
110 g2d.fill(getShape());
111 }
112
113 public void draw(Graphics2D g2d) {
114 g2d.draw(getShape());
115 }
116 }
117
118 public static void usage(String err) {
119 if (err != null) {
120 System.err.println(err);
121 }
122 System.err.println("usage: java RenderClipTest "+
123 "[-read[file F]] [-rectsuite] [-fill] [-draw]");
124 System.err.println(" "+
125 "[-aa] [-pure] [-lw N] [-rot N]");
126 System.err.println(" "+
127 "[-rectmethod] [-linemethod] [-rect] [-line]");
128 System.err.println(" "+
129 "[-cubic] [-quad] [-poly] [-path]");
130 System.err.println(" "+
131 "[-silent] [-verbose] [-showerr] [-count N]");
132 System.err.println(" "+
133 "[-strict] [-usage]");
134 System.err.println(" -read Read test data from stdin");
135 System.err.println(" -readfile F Read test data from file F");
136 System.err.println(" -rectsuite Run a suite of rect/line tests");
137 System.err.println(" -fill Test g.fill*(...)");
138 System.err.println(" -draw Test g.draw*(...)");
139 System.err.println(" -aa Use antialiased rendering");
140 System.err.println(" -pure Use STROKE_PURE hint");
141 System.err.println(" -lw N Test line widths of N "+
142 "(default 1.0)");
143 System.err.println(" -rot N Test rotation by N degrees "+
144 "(default 0.0)");
145 System.err.println(" -rectmethod Test fillRect/drawRect methods");
146 System.err.println(" -linemethod Test drawLine method");
147 System.err.println(" -rect Test Rectangle2D shapes");
148 System.err.println(" -line Test Line2D shapes");
149 System.err.println(" -cubic Test CubicCurve2D shapes");
150 System.err.println(" -quad Test QuadCurve2D shapes");
151 System.err.println(" -poly Test Polygon shapes");
152 System.err.println(" -path Test GeneralPath shapes");
153 System.err.println(" -silent Do not print out error curves");
154 System.err.println(" -verbose Print out progress info");
155 System.err.println(" -showerr Display errors on screen");
156 System.err.println(" -count N N tests per shape, then exit "+
157 "(default 1000)");
158 System.err.println(" -strict All failures are important");
159 System.err.println(" -usage Print this help, then exit");
160 System.exit((err != null) ? -1 : 0);
161 }
162
163 public static void main(String argv[]) {
164 boolean readTests = false;
165 String readFile = null;
166 boolean rectsuite = false;
167 int count = 1000;
168 lw = 1.0f;
169 rot = 0.0;
170 Vector<AnnotatedRenderOp> testOps = new Vector<AnnotatedRenderOp>();
171 for (int i = 0; i < argv.length; i++) {
172 String arg = argv[i].toLowerCase();
173 if (arg.equals("-aa")) {
174 useAA = true;
175 } else if (arg.equals("-pure")) {
176 strokePure = true;
177 } else if (arg.equals("-fill")) {
178 testFill = true;
179 } else if (arg.equals("-draw")) {
180 testDraw = true;
181 } else if (arg.equals("-lw")) {
182 if (i+1 >= argv.length) {
183 usage("Missing argument: "+argv[i]);
184 }
185 lw = Float.parseFloat(argv[++i]);
186 } else if (arg.equals("-rot")) {
187 if (i+1 >= argv.length) {
188 usage("Missing argument: "+argv[i]);
189 }
190 rot = Double.parseDouble(argv[++i]);
191 } else if (arg.equals("-cubic")) {
192 testOps.add(new Cubic());
193 } else if (arg.equals("-quad")) {
194 testOps.add(new Quad());
195 } else if (arg.equals("-poly")) {
196 testOps.add(new Poly());
197 } else if (arg.equals("-path")) {
198 testOps.add(new Path());
199 } else if (arg.equals("-rect")) {
200 testOps.add(new Rect());
201 } else if (arg.equals("-line")) {
202 testOps.add(new Line());
203 } else if (arg.equals("-rectmethod")) {
204 testOps.add(new RectMethod());
205 } else if (arg.equals("-linemethod")) {
206 testOps.add(new LineMethod());
207 } else if (arg.equals("-verbose")) {
208 verbose = true;
209 } else if (arg.equals("-strict")) {
210 strict = true;
211 } else if (arg.equals("-silent")) {
212 silent = true;
213 } else if (arg.equals("-showerr")) {
214 showErrors = true;
215 } else if (arg.equals("-readfile")) {
216 if (i+1 >= argv.length) {
217 usage("Missing argument: "+argv[i]);
218 }
219 readTests = true;
220 readFile = argv[++i];
221 } else if (arg.equals("-read")) {
222 readTests = true;
223 readFile = null;
224 } else if (arg.equals("-rectsuite")) {
225 rectsuite = true;
226 } else if (arg.equals("-count")) {
227 if (i+1 >= argv.length) {
228 usage("Missing argument: "+argv[i]);
229 }
230 count = Integer.parseInt(argv[++i]);
231 } else if (arg.equals("-usage")) {
232 usage(null);
233 } else {
234 usage("Unknown argument: "+argv[i]);
235 }
236 }
237 if (readTests) {
238 if (rectsuite || testDraw || testFill ||
239 useAA || strokePure ||
240 lw != 1.0f || rot != 0.0 ||
241 testOps.size() > 0)
242 {
243 usage("Should not specify test types with -read options");
244 }
245 } else if (rectsuite) {
246 if (testDraw || testFill ||
247 useAA || strokePure ||
248 lw != 1.0f || rot != 0.0 ||
249 testOps.size() > 0)
250 {
251 usage("Should not specify test types with -rectsuite option");
252 }
253 } else {
254 if (!testDraw && !testFill) {
255 usage("No work: Must specify one or both of "+
256 "-fill or -draw");
257 }
258 if (testOps.size() == 0) {
259 usage("No work: Must specify one or more of "+
260 "-rect[method], -line[method], "+
261 "-cubic, -quad, -poly, or -path");
262 }
263 }
264 initImages();
265 if (readTests) {
266 try {
267 InputStream is;
268 if (readFile == null) {
269 is = System.in;
270 } else {
271 File f =
272 new File(System.getProperty("test.src", "."),
273 readFile);
274 is = new FileInputStream(f);
275 }
276 parseAndRun(is);
277 } catch (IOException e) {
278 throw new RuntimeException(e);
279 }
280 } else if (rectsuite) {
281 runRectSuite(count);
282 } else {
283 initGCs();
284 for (int k = 0; k < testOps.size(); k++) {
285 AnnotatedRenderOp ar = testOps.get(k);
286 runRandomTests(ar, count);
287 }
288 disposeGCs();
289 }
290 grefclear.dispose();
291 gtstclear.dispose();
292 grefclear = gtstclear = null;
293 reportStatistics();
294 }
295
296 public static int reportStatistics() {
297 String connector = "";
298 if (numfillfailures > 0) {
299 System.out.print(numfillfailures+" fills ");
300 connector = "and ";
301 }
302 if (numstrokefailures > 0) {
303 System.out.print(connector+numstrokefailures+" strokes ");
304 }
305 int totalfailures = numfillfailures + numstrokefailures;
306 if (totalfailures == 0) {
307 System.out.print("0 ");
308 }
309 System.out.println("out of "+numtests+" tests failed...");
310 int critical = numerrors;
311 if (strict) {
312 critical += totalfailures;
313 }
314 if (critical > 0) {
315 throw new RuntimeException(critical+" tests had critical errors");
316 }
317 System.out.println("No tests had critical errors");
318 return (numerrors+totalfailures);
319 }
320
321 public static void runRectSuite(int count) {
322 AnnotatedRenderOp ops[] = {
323 new Rect(),
324 new RectMethod(),
325 new Line(),
326 new LineMethod(),
327 };
328
329
330 float filllinewidths[] = { 0.0f, 2.0f };
331 float drawlinewidths[] = { 0.0f, 0.5f, 1.0f,
332 2.0f, 2.5f,
333 5.0f, 5.3f };
334 double rotations[] = { 0.0, 15.0, 90.0,
335 135.0, 180.0,
336 200.0, 270.0,
337 300.0};
338 for (AnnotatedRenderOp ar: ops) {
339 for (double r: rotations) {
340 rot = r;
341 for (int i = 0; i < 8; i++) {
342 float linewidths[];
343 if ((i & 1) == 0) {
344 if ((ar instanceof Line) ||
345 (ar instanceof LineMethod))
346 {
347 continue;
348 }
349 testFill = true;
350 testDraw = false;
351 linewidths = filllinewidths;
352 } else {
353 testFill = false;
354 testDraw = true;
355 linewidths = drawlinewidths;
356 }
357 useAA = ((i & 2) != 0);
358 strokePure = ((i & 4) != 0);
359 for (float w : linewidths) {
360 lw = w;
361 runSuiteTests(ar, count);
362 }
363 }
364 }
365 }
366 }
367
368 public static void runSuiteTests(AnnotatedRenderOp ar, int count) {
369 if (verbose) {
370 System.out.print("Running ");
371 System.out.print(testFill ? "Fill " : "Draw ");
372 System.out.print(BaseName(ar));
373 if (useAA) {
374 System.out.print(" AA");
375 }
376 if (strokePure) {
377 System.out.print(" Pure");
378 }
379 if (lw != 1.0f) {
380 System.out.print(" lw="+lw);
381 }
382 if (rot != 0.0f) {
383 System.out.print(" rot="+rot);
384 }
385 System.out.println();
386 }
387 initGCs();
388 runRandomTests(ar, count);
389 disposeGCs();
390 }
391
392 public static String BaseName(AnnotatedRenderOp ar) {
393 String s = ar.toString();
394 int leftparen = s.indexOf('(');
395 if (leftparen >= 0) {
396 s = s.substring(0, leftparen);
397 }
398 return s;
399 }
400
401 public static void runRandomTests(AnnotatedRenderOp ar, int count) {
402 for (int i = 0; i < count; i++) {
403 ar.randomize();
404 if (testDraw) {
405 test(ar, false);
406 }
407 if (testFill) {
408 test(ar, true);
409 }
410 }
411 }
412
413 public static void initImages() {
414 imgref = new BufferedImage(40, 40, BufferedImage.TYPE_INT_RGB);
415 imgtst = new BufferedImage(40, 40, BufferedImage.TYPE_INT_RGB);
416 grefclear = imgref.createGraphics();
417 gtstclear = imgtst.createGraphics();
418 grefclear.setColor(Color.white);
419 gtstclear.setColor(Color.white);
420 }
421
422 public static void initGCs() {
423 grefrender = imgref.createGraphics();
424 gtstrender = imgtst.createGraphics();
425 gtstrender.clipRect(10, 10, 20, 20);
426 grefrender.setColor(Color.blue);
427 gtstrender.setColor(Color.blue);
428 if (lw != 1.0f) {
429 BasicStroke bs = new BasicStroke(lw);
430 grefrender.setStroke(bs);
431 gtstrender.setStroke(bs);
432 }
433 if (rot != 0.0) {
434 double rotrad = Math.toRadians(rot);
435 grefrender.rotate(rotrad, 20, 20);
436 gtstrender.rotate(rotrad, 20, 20);
437 }
438 if (strokePure) {
439 grefrender.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
440 RenderingHints.VALUE_STROKE_PURE);
441 gtstrender.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
442 RenderingHints.VALUE_STROKE_PURE);
443 }
444 if (useAA) {
445 grefrender.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
446 RenderingHints.VALUE_ANTIALIAS_ON);
447 gtstrender.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
448 RenderingHints.VALUE_ANTIALIAS_ON);
449 maxerr = 1;
450 }
451 }
452
453 public static void disposeGCs() {
454 grefrender.dispose();
455 gtstrender.dispose();
456 grefrender = gtstrender = null;
457 }
458
459 public static void parseAndRun(InputStream in) throws IOException {
460 BufferedReader br = new BufferedReader(new InputStreamReader(in));
461 String str;
462 while ((str = br.readLine()) != null) {
463 if (str.startsWith("Stroked ") || str.startsWith("Filled ")) {
464 parseTest(str);
465 continue;
466 }
467 if (str.startsWith("Running ")) {
468 continue;
469 }
470 if (str.startsWith("Failed: ")) {
471 continue;
472 }
473 if (str.indexOf(" out of ") > 0 &&
474 str.indexOf(" tests failed...") > 0)
475 {
476 continue;
477 }
478 if (str.indexOf(" tests had critical errors") > 0) {
479 continue;
480 }
481 System.err.println("Unparseable line: "+str);
482 }
483 }
484
485 public static void parseTest(String origstr) {
486 String str = origstr;
487 boolean isfill = false;
488 useAA = strokePure = false;
489 lw = 1.0f;
490 rot = 0.0;
491 if (str.startsWith("Stroked ")) {
492 str = str.substring(8);
493 isfill = false;
494 } else if (str.startsWith("Filled ")) {
495 str = str.substring(7);
496 isfill = true;
497 } else {
498 System.err.println("Unparseable test line: "+origstr);
499 }
500 if (str.startsWith("AA ")) {
501 str = str.substring(3);
502 useAA = true;
503 }
504 if (str.startsWith("Pure ")) {
505 str = str.substring(5);
506 strokePure = true;
507 }
508 if (str.startsWith("Lw=")) {
509 int index = str.indexOf(' ', 3);
510 if (index > 0) {
511 lw = Float.parseFloat(str.substring(3, index));
512 str = str.substring(index+1);
513 }
514 }
515 if (str.startsWith("Rot=")) {
516 int index = str.indexOf(' ', 4);
517 if (index > 0) {
518 rot = Double.parseDouble(str.substring(4, index));
519 str = str.substring(index+1);
520 }
521 }
522 AnnotatedRenderOp ar = AnnotatedRenderOp.parse(str);
523 if (ar != null) {
524 initGCs();
525 test(ar, isfill);
526 disposeGCs();
527 } else {
528 System.err.println("Unparseable test line: "+origstr);
529 }
530 }
531
532 public static void test(AnnotatedRenderOp ar, boolean isfill) {
533 grefclear.fillRect(0, 0, 40, 40);
534 gtstclear.fillRect(0, 0, 40, 40);
535 if (isfill) {
536 ar.fill(grefrender);
537 ar.fill(gtstrender);
538 } else {
539 ar.draw(grefrender);
540 ar.draw(gtstrender);
541 }
542 check(imgref, imgtst, ar, isfill);
543 }
544
545 public static int[] getData(BufferedImage img) {
546 Raster r = img.getRaster();
547 DataBufferInt dbi = (DataBufferInt) r.getDataBuffer();
548 return dbi.getData();
549 }
550
551 public static int getScan(BufferedImage img) {
552 Raster r = img.getRaster();
553 SinglePixelPackedSampleModel sppsm =
554 (SinglePixelPackedSampleModel) r.getSampleModel();
555 return sppsm.getScanlineStride();
556 }
557
558 public static int getOffset(BufferedImage img) {
559 Raster r = img.getRaster();
560 SinglePixelPackedSampleModel sppsm =
561 (SinglePixelPackedSampleModel) r.getSampleModel();
562 return sppsm.getOffset(-r.getSampleModelTranslateX(),
563 -r.getSampleModelTranslateY());
564 }
565
566 final static int opaque = 0xff000000;
567 final static int whitergb = Color.white.getRGB();
568
569 public static final int maxdiff(int rgb1, int rgb2) {
570 int maxd = 0;
571 for (int i = 0; i < 32; i += 8) {
572 int c1 = (rgb1 >> i) & 0xff;
573 int c2 = (rgb2 >> i) & 0xff;
574 int d = Math.abs(c1-c2);
575 if (maxd < d) {
576 maxd = d;
577 }
578 }
579 return maxd;
580 }
581
582 public static void check(BufferedImage imgref, BufferedImage imgtst,
583 AnnotatedRenderOp ar, boolean wasfill)
584 {
585 numtests++;
586 int dataref[] = getData(imgref);
587 int datatst[] = getData(imgtst);
588 int scanref = getScan(imgref);
589 int scantst = getScan(imgtst);
590 int offref = getOffset(imgref);
591 int offtst = getOffset(imgtst);
592
593
594
595
596
597
598 if (check(ar, wasfill,
599 null, 0, 0,
600 datatst, scantst, offtst,
601 0, 0, 40, 10))
602 {
603 return;
604 }
605
606 if (check(ar, wasfill,
607 null, 0, 0,
608 datatst, scantst, offtst,
609 0, 30, 40, 40))
610 {
611 return;
612 }
613
614 if (check(ar, wasfill,
615 null, 0, 0,
616 datatst, scantst, offtst,
617 0, 10, 10, 30))
618 {
619 return;
620 }
621
622 if (check(ar, wasfill,
623 null, 0, 0,
624 datatst, scantst, offtst,
625 30, 10, 40, 30))
626 {
627 return;
628 }
629
630 check(ar, wasfill,
631 dataref, scanref, offref,
632 datatst, scantst, offtst,
633 10, 10, 30, 30);
634 }
635
636 public static boolean check(AnnotatedRenderOp ar, boolean wasfill,
637 int dataref[], int scanref, int offref,
638 int datatst[], int scantst, int offtst,
639 int x0, int y0, int x1, int y1)
640 {
641 offref += scanref * y0;
642 offtst += scantst * y0;
643 for (int y = y0; y < y1; y++) {
644 for (int x = x0; x < x1; x++) {
645 boolean failed;
646 String reason;
647 int rgbref;
648 int rgbtst;
649
650 rgbtst = datatst[offtst+x] | opaque;
651 if (dataref == null) {
652
653 rgbref = whitergb;
654 failed = (rgbtst != rgbref);
655 reason = "stray pixel rendered outside of clip";
656 } else {
657
658 rgbref = dataref[offref+x] | opaque;
659 failed = (rgbref != rgbtst &&
660 maxdiff(rgbref, rgbtst) > maxerr);
661 reason = "different pixel rendered inside clip";
662 }
663 if (failed) {
664 if (dataref == null) {
665 numerrors++;
666 }
667 if (wasfill) {
668 numfillfailures++;
669 } else {
670 numstrokefailures++;
671 }
672 if (!silent) {
673 System.out.println("Failed: "+reason+" at "+x+", "+y+
674 " ["+Integer.toHexString(rgbref)+
675 " != "+Integer.toHexString(rgbtst)+
676 "]");
677 System.out.print(wasfill ? "Filled " : "Stroked ");
678 if (useAA) System.out.print("AA ");
679 if (strokePure) System.out.print("Pure ");
680 if (lw != 1) System.out.print("Lw="+lw+" ");
681 if (rot != 0) System.out.print("Rot="+rot+" ");
682 System.out.println(ar);
683 }
684 if (showErrors) {
685 show(imgref, imgtst);
686 }
687 return true;
688 }
689 }
690 offref += scanref;
691 offtst += scantst;
692 }
693 return false;
694 }
695
696 static ErrorWindow errw;
697
698 public static void show(BufferedImage imgref, BufferedImage imgtst) {
699 ErrorWindow errw = new ErrorWindow();
700 errw.setImages(imgref, imgtst);
701 errw.setVisible(true);
702 errw.waitForHide();
703 errw.dispose();
704 }
705
706 public static class Cubic extends AnnotatedShapeOp {
707 public static Cubic tryparse(String str) {
708 str = str.trim();
709 if (!str.startsWith("Cubic(")) {
710 return null;
711 }
712 str = str.substring(6);
713 double coords[] = new double[8];
714 boolean foundparen = false;
715 for (int i = 0; i < coords.length; i++) {
716 int index = str.indexOf(",");
717 if (index < 0) {
718 if (i < coords.length-1) {
719 return null;
720 }
721 index = str.indexOf(")");
722 if (index < 0) {
723 return null;
724 }
725 foundparen = true;
726 }
727 String num = str.substring(0, index);
728 try {
729 coords[i] = Double.parseDouble(num);
730 } catch (NumberFormatException nfe) {
731 return null;
732 }
733 str = str.substring(index+1);
734 }
735 if (!foundparen || str.length() > 0) {
736 return null;
737 }
738 Cubic c = new Cubic();
739 c.cubic.setCurve(coords[0], coords[1],
740 coords[2], coords[3],
741 coords[4], coords[5],
742 coords[6], coords[7]);
743 return c;
744 }
745
746 private CubicCurve2D cubic = new CubicCurve2D.Double();
747
748 public void randomize() {
749 cubic.setCurve(randDblCoord(), randDblCoord(),
750 randDblCoord(), randDblCoord(),
751 randDblCoord(), randDblCoord(),
752 randDblCoord(), randDblCoord());
753 }
754
755 public Shape getShape() {
756 return cubic;
757 }
758
759 public String toString() {
760 return ("Cubic("+
761 cubic.getX1()+", "+
762 cubic.getY1()+", "+
763 cubic.getCtrlX1()+", "+
764 cubic.getCtrlY1()+", "+
765 cubic.getCtrlX2()+", "+
766 cubic.getCtrlY2()+", "+
767 cubic.getX2()+", "+
768 cubic.getY2()
769 +")");
770 }
771 }
772
773 public static class Quad extends AnnotatedShapeOp {
774 public static Quad tryparse(String str) {
775 str = str.trim();
776 if (!str.startsWith("Quad(")) {
777 return null;
778 }
779 str = str.substring(5);
780 double coords[] = new double[6];
781 boolean foundparen = false;
782 for (int i = 0; i < coords.length; i++) {
783 int index = str.indexOf(",");
784 if (index < 0) {
785 if (i < coords.length-1) {
786 return null;
787 }
788 index = str.indexOf(")");
789 if (index < 0) {
790 return null;
791 }
792 foundparen = true;
793 }
794 String num = str.substring(0, index);
795 try {
796 coords[i] = Double.parseDouble(num);
797 } catch (NumberFormatException nfe) {
798 return null;
799 }
800 str = str.substring(index+1);
801 }
802 if (!foundparen || str.length() > 0) {
803 return null;
804 }
805 Quad c = new Quad();
806 c.quad.setCurve(coords[0], coords[1],
807 coords[2], coords[3],
808 coords[4], coords[5]);
809 return c;
810 }
811
812 private QuadCurve2D quad = new QuadCurve2D.Double();
813
814 public void randomize() {
815 quad.setCurve(randDblCoord(), randDblCoord(),
816 randDblCoord(), randDblCoord(),
817 randDblCoord(), randDblCoord());
818 }
819
820 public Shape getShape() {
821 return quad;
822 }
823
824 public String toString() {
825 return ("Quad("+
826 quad.getX1()+", "+
827 quad.getY1()+", "+
828 quad.getCtrlX()+", "+
829 quad.getCtrlY()+", "+
830 quad.getX2()+", "+
831 quad.getY2()
832 +")");
833 }
834 }
835
836 public static class Poly extends AnnotatedShapeOp {
837 public static Poly tryparse(String str) {
838 str = str.trim();
839 if (!str.startsWith("Poly(")) {
840 return null;
841 }
842 str = str.substring(5);
843 Polygon p = new Polygon();
844 while (true) {
845 int x, y;
846 str = str.trim();
847 if (str.startsWith(")")) {
848 str = str.substring(1);
849 break;
850 }
851 if (p.npoints > 0) {
852 if (str.startsWith(",")) {
853 str = str.substring(2).trim();
854 } else {
855 return null;
856 }
857 }
858 if (str.startsWith("[")) {
859 str = str.substring(1);
860 } else {
861 return null;
862 }
863 int index = str.indexOf(",");
864 if (index < 0) {
865 return null;
866 }
867 String num = str.substring(0, index);
868 try {
869 x = Integer.parseInt(num);
870 } catch (NumberFormatException nfe) {
871 return null;
872 }
873 str = str.substring(index+1);
874 index = str.indexOf("]");
875 if (index < 0) {
876 return null;
877 }
878 num = str.substring(0, index).trim();
879 try {
880 y = Integer.parseInt(num);
881 } catch (NumberFormatException nfe) {
882 return null;
883 }
884 str = str.substring(index+1);
885 p.addPoint(x, y);
886 }
887 if (str.length() > 0) {
888 return null;
889 }
890 if (p.npoints < 3) {
891 return null;
892 }
893 return new Poly(p);
894 }
895
896 private Polygon poly;
897
898 public Poly() {
899 this.poly = new Polygon();
900 }
901
902 private Poly(Polygon p) {
903 this.poly = p;
904 }
905
906 public void randomize() {
907 poly.reset();
908 poly.addPoint(randIntCoord(), randIntCoord());
909 poly.addPoint(randIntCoord(), randIntCoord());
910 poly.addPoint(randIntCoord(), randIntCoord());
911 poly.addPoint(randIntCoord(), randIntCoord());
912 poly.addPoint(randIntCoord(), randIntCoord());
913 }
914
915 public Shape getShape() {
916 return poly;
917 }
918
919 public String toString() {
920 StringBuffer sb = new StringBuffer(100);
921 sb.append("Poly(");
922 for (int i = 0; i < poly.npoints; i++) {
923 if (i != 0) {
924 sb.append(", ");
925 }
926 sb.append("[");
927 sb.append(poly.xpoints[i]);
928 sb.append(", ");
929 sb.append(poly.ypoints[i]);
930 sb.append("]");
931 }
932 sb.append(")");
933 return sb.toString();
934 }
935 }
936
937 public static class Path extends AnnotatedShapeOp {
938 public static Path tryparse(String str) {
939 str = str.trim();
940 if (!str.startsWith("Path(")) {
941 return null;
942 }
943 str = str.substring(5);
944 GeneralPath gp = new GeneralPath();
945 float coords[] = new float[6];
946 int numsegs = 0;
947 while (true) {
948 int type;
949 int n;
950 str = str.trim();
951 if (str.startsWith(")")) {
952 str = str.substring(1);
953 break;
954 }
955 if (str.startsWith("M[")) {
956 type = PathIterator.SEG_MOVETO;
957 n = 2;
958 } else if (str.startsWith("L[")) {
959 type = PathIterator.SEG_LINETO;
960 n = 2;
961 } else if (str.startsWith("Q[")) {
962 type = PathIterator.SEG_QUADTO;
963 n = 4;
964 } else if (str.startsWith("C[")) {
965 type = PathIterator.SEG_CUBICTO;
966 n = 6;
967 } else if (str.startsWith("E[")) {
968 type = PathIterator.SEG_CLOSE;
969 n = 0;
970 } else {
971 return null;
972 }
973 str = str.substring(2);
974 if (n == 0) {
975 if (str.startsWith("]")) {
976 str = str.substring(1);
977 } else {
978 return null;
979 }
980 }
981 for (int i = 0; i < n; i++) {
982 int index;
983 if (i < n-1) {
984 index = str.indexOf(",");
985 } else {
986 index = str.indexOf("]");
987 }
988 if (index < 0) {
989 return null;
990 }
991 String num = str.substring(0, index);
992 try {
993 coords[i] = Float.parseFloat(num);
994 } catch (NumberFormatException nfe) {
995 return null;
996 }
997 str = str.substring(index+1).trim();
998 }
999 switch (type) {
1000 case PathIterator.SEG_MOVETO:
1001 gp.moveTo(coords[0], coords[1]);
1002 break;
1003 case PathIterator.SEG_LINETO:
1004 gp.lineTo(coords[0], coords[1]);
1005 break;
1006 case PathIterator.SEG_QUADTO:
1007 gp.quadTo(coords[0], coords[1],
1008 coords[2], coords[3]);
1009 break;
1010 case PathIterator.SEG_CUBICTO:
1011 gp.curveTo(coords[0], coords[1],
1012 coords[2], coords[3],
1013 coords[4], coords[5]);
1014 break;
1015 case PathIterator.SEG_CLOSE:
1016 gp.closePath();
1017 break;
1018 }
1019 numsegs++;
1020 }
1021 if (str.length() > 0) {
1022 return null;
1023 }
1024 if (numsegs < 2) {
1025 return null;
1026 }
1027 return new Path(gp);
1028 }
1029
1030 private GeneralPath path;
1031
1032 public Path() {
1033 this.path = new GeneralPath();
1034 }
1035
1036 private Path(GeneralPath gp) {
1037 this.path = gp;
1038 }
1039
1040 public void randomize() {
1041 path.reset();
1042 path.moveTo(randFltCoord(), randFltCoord());
1043 for (int i = randInt(5)+3; i > 0; --i) {
1044 switch(randInt(5)) {
1045 case 0:
1046 path.moveTo(randFltCoord(), randFltCoord());
1047 break;
1048 case 1:
1049 path.lineTo(randFltCoord(), randFltCoord());
1050 break;
1051 case 2:
1052 path.quadTo(randFltCoord(), randFltCoord(),
1053 randFltCoord(), randFltCoord());
1054 break;
1055 case 3:
1056 path.curveTo(randFltCoord(), randFltCoord(),
1057 randFltCoord(), randFltCoord(),
1058 randFltCoord(), randFltCoord());
1059 break;
1060 case 4:
1061 path.closePath();
1062 break;
1063 }
1064 }
1065 }
1066
1067 public Shape getShape() {
1068 return path;
1069 }
1070
1071 public String toString() {
1072 StringBuffer sb = new StringBuffer(100);
1073 sb.append("Path(");
1074 PathIterator pi = path.getPathIterator(null);
1075 float coords[] = new float[6];
1076 boolean first = true;
1077 while (!pi.isDone()) {
1078 int n;
1079 char c;
1080 switch(pi.currentSegment(coords)) {
1081 case PathIterator.SEG_MOVETO:
1082 c = 'M';
1083 n = 2;
1084 break;
1085 case PathIterator.SEG_LINETO:
1086 c = 'L';
1087 n = 2;
1088 break;
1089 case PathIterator.SEG_QUADTO:
1090 c = 'Q';
1091 n = 4;
1092 break;
1093 case PathIterator.SEG_CUBICTO:
1094 c = 'C';
1095 n = 6;
1096 break;
1097 case PathIterator.SEG_CLOSE:
1098 c = 'E';
1099 n = 0;
1100 break;
1101 default:
1102 throw new InternalError("Unknown segment!");
1103 }
1104 sb.append(c);
1105 sb.append("[");
1106 for (int i = 0; i < n; i++) {
1107 if (i != 0) {
1108 sb.append(",");
1109 }
1110 sb.append(coords[i]);
1111 }
1112 sb.append("]");
1113 pi.next();
1114 }
1115 sb.append(")");
1116 return sb.toString();
1117 }
1118 }
1119
1120 public static class Rect extends AnnotatedShapeOp {
1121 public static Rect tryparse(String str) {
1122 str = str.trim();
1123 if (!str.startsWith("Rect(")) {
1124 return null;
1125 }
1126 str = str.substring(5);
1127 double coords[] = new double[4];
1128 boolean foundparen = false;
1129 for (int i = 0; i < coords.length; i++) {
1130 int index = str.indexOf(",");
1131 if (index < 0) {
1132 if (i < coords.length-1) {
1133 return null;
1134 }
1135 index = str.indexOf(")");
1136 if (index < 0) {
1137 return null;
1138 }
1139 foundparen = true;
1140 }
1141 String num = str.substring(0, index);
1142 try {
1143 coords[i] = Double.parseDouble(num);
1144 } catch (NumberFormatException nfe) {
1145 return null;
1146 }
1147 str = str.substring(index+1);
1148 }
1149 if (!foundparen || str.length() > 0) {
1150 return null;
1151 }
1152 Rect r = new Rect();
1153 r.rect.setRect(coords[0], coords[1],
1154 coords[2], coords[3]);
1155 return r;
1156 }
1157
1158 private Rectangle2D rect = new Rectangle2D.Double();
1159
1160 public void randomize() {
1161 rect.setRect(randDblCoord(), randDblCoord(),
1162 randDblCoord(), randDblCoord());
1163 }
1164
1165 public Shape getShape() {
1166 return rect;
1167 }
1168
1169 public String toString() {
1170 return ("Rect("+
1171 rect.getX()+", "+
1172 rect.getY()+", "+
1173 rect.getWidth()+", "+
1174 rect.getHeight()
1175 +")");
1176 }
1177 }
1178
1179 public static class Line extends AnnotatedShapeOp {
1180 public static Line tryparse(String str) {
1181 str = str.trim();
1182 if (!str.startsWith("Line(")) {
1183 return null;
1184 }
1185 str = str.substring(5);
1186 double coords[] = new double[4];
1187 boolean foundparen = false;
1188 for (int i = 0; i < coords.length; i++) {
1189 int index = str.indexOf(",");
1190 if (index < 0) {
1191 if (i < coords.length-1) {
1192 return null;
1193 }
1194 index = str.indexOf(")");
1195 if (index < 0) {
1196 return null;
1197 }
1198 foundparen = true;
1199 }
1200 String num = str.substring(0, index);
1201 try {
1202 coords[i] = Double.parseDouble(num);
1203 } catch (NumberFormatException nfe) {
1204 return null;
1205 }
1206 str = str.substring(index+1);
1207 }
1208 if (!foundparen || str.length() > 0) {
1209 return null;
1210 }
1211 Line l = new Line();
1212 l.line.setLine(coords[0], coords[1],
1213 coords[2], coords[3]);
1214 return l;
1215 }
1216
1217 private Line2D line = new Line2D.Double();
1218
1219 public void randomize() {
1220 line.setLine(randDblCoord(), randDblCoord(),
1221 randDblCoord(), randDblCoord());
1222 }
1223
1224 public Shape getShape() {
1225 return line;
1226 }
1227
1228 public String toString() {
1229 return ("Line("+
1230 line.getX1()+", "+
1231 line.getY1()+", "+
1232 line.getX2()+", "+
1233 line.getY2()
1234 +")");
1235 }
1236 }
1237
1238 public static class RectMethod extends AnnotatedRenderOp {
1239 public static RectMethod tryparse(String str) {
1240 str = str.trim();
1241 if (!str.startsWith("RectMethod(")) {
1242 return null;
1243 }
1244 str = str.substring(11);
1245 int coords[] = new int[4];
1246 boolean foundparen = false;
1247 for (int i = 0; i < coords.length; i++) {
1248 int index = str.indexOf(",");
1249 if (index < 0) {
1250 if (i < coords.length-1) {
1251 return null;
1252 }
1253 index = str.indexOf(")");
1254 if (index < 0) {
1255 return null;
1256 }
1257 foundparen = true;
1258 }
1259 String num = str.substring(0, index).trim();
1260 try {
1261 coords[i] = Integer.parseInt(num);
1262 } catch (NumberFormatException nfe) {
1263 return null;
1264 }
1265 str = str.substring(index+1);
1266 }
1267 if (!foundparen || str.length() > 0) {
1268 return null;
1269 }
1270 RectMethod rm = new RectMethod();
1271 rm.rect.setBounds(coords[0], coords[1],
1272 coords[2], coords[3]);
1273 return rm;
1274 }
1275
1276 private Rectangle rect = new Rectangle();
1277
1278 public void randomize() {
1279 rect.setBounds(randIntCoord(), randIntCoord(),
1280 randIntCoord(), randIntCoord());
1281 }
1282
1283 public void fill(Graphics2D g2d) {
1284 g2d.fillRect(rect.x, rect.y, rect.width, rect.height);
1285 }
1286
1287 public void draw(Graphics2D g2d) {
1288 g2d.drawRect(rect.x, rect.y, rect.width, rect.height);
1289 }
1290
1291 public String toString() {
1292 return ("RectMethod("+
1293 rect.x+", "+
1294 rect.y+", "+
1295 rect.width+", "+
1296 rect.height
1297 +")");
1298 }
1299 }
1300
1301 public static class LineMethod extends AnnotatedRenderOp {
1302 public static LineMethod tryparse(String str) {
1303 str = str.trim();
1304 if (!str.startsWith("LineMethod(")) {
1305 return null;
1306 }
1307 str = str.substring(11);
1308 int coords[] = new int[4];
1309 boolean foundparen = false;
1310 for (int i = 0; i < coords.length; i++) {
1311 int index = str.indexOf(",");
1312 if (index < 0) {
1313 if (i < coords.length-1) {
1314 return null;
1315 }
1316 index = str.indexOf(")");
1317 if (index < 0) {
1318 return null;
1319 }
1320 foundparen = true;
1321 }
1322 String num = str.substring(0, index).trim();
1323 try {
1324 coords[i] = Integer.parseInt(num);
1325 } catch (NumberFormatException nfe) {
1326 return null;
1327 }
1328 str = str.substring(index+1);
1329 }
1330 if (!foundparen || str.length() > 0) {
1331 return null;
1332 }
1333 LineMethod lm = new LineMethod();
1334 lm.line = coords;
1335 return lm;
1336 }
1337
1338 private int line[] = new int[4];
1339
1340 public void randomize() {
1341 line[0] = randIntCoord();
1342 line[1] = randIntCoord();
1343 line[2] = randIntCoord();
1344 line[3] = randIntCoord();
1345 }
1346
1347 public void fill(Graphics2D g2d) {
1348 }
1349
1350 public void draw(Graphics2D g2d) {
1351 g2d.drawLine(line[0], line[1], line[2], line[3]);
1352 }
1353
1354 public String toString() {
1355 return ("LineMethod("+
1356 line[0]+", "+
1357 line[1]+", "+
1358 line[2]+", "+
1359 line[3]
1360 +")");
1361 }
1362 }
1363
1364 public static class ErrorWindow extends Frame {
1365 ImageCanvas unclipped;
1366 ImageCanvas reference;
1367 ImageCanvas actual;
1368 ImageCanvas diff;
1369
1370 public ErrorWindow() {
1371 super("Error Comparison Window");
1372
1373 unclipped = new ImageCanvas();
1374 reference = new ImageCanvas();
1375 actual = new ImageCanvas();
1376 diff = new ImageCanvas();
1377
1378 setLayout(new SmartGridLayout(0, 2, 5, 5));
1379 addImagePanel(unclipped, "Unclipped rendering");
1380 addImagePanel(reference, "Clipped reference");
1381 addImagePanel(actual, "Actual clipped");
1382 addImagePanel(diff, "Difference");
1383
1384 addWindowListener(new WindowAdapter() {
1385 public void windowClosing(WindowEvent e) {
1386 setVisible(false);
1387 }
1388 });
1389 }
1390
1391 public void addImagePanel(ImageCanvas ic, String label) {
1392 add(ic);
1393 add(new Label(label));
1394 }
1395
1396 public void setImages(BufferedImage imgref, BufferedImage imgtst) {
1397 unclipped.setImage(imgref);
1398 reference.setReference(imgref);
1399 actual.setImage(imgtst);
1400 diff.setDiff(reference.getImage(), imgtst);
1401 invalidate();
1402 pack();
1403 repaint();
1404 }
1405
1406 public void setVisible(boolean vis) {
1407 super.setVisible(vis);
1408 synchronized (this) {
1409 notifyAll();
1410 }
1411 }
1412
1413 public synchronized void waitForHide() {
1414 while (isShowing()) {
1415 try {
1416 wait();
1417 } catch (InterruptedException e) {
1418 System.exit(2);
1419 }
1420 }
1421 }
1422 }
1423
1424 public static class SmartGridLayout implements LayoutManager {
1425 int rows;
1426 int cols;
1427 int hgap;
1428 int vgap;
1429
1430 public SmartGridLayout(int r, int c, int h, int v) {
1431 this.rows = r;
1432 this.cols = c;
1433 this.hgap = h;
1434 this.vgap = v;
1435 }
1436
1437 public void addLayoutComponent(String name, Component comp) {
1438 }
1439
1440 public void removeLayoutComponent(Component comp) {
1441 }
1442
1443 public int[][] getGridSizes(Container parent, boolean min) {
1444 int ncomponents = parent.getComponentCount();
1445 int nrows = rows;
1446 int ncols = cols;
1447
1448 if (nrows > 0) {
1449 ncols = (ncomponents + nrows - 1) / nrows;
1450 } else {
1451 nrows = (ncomponents + ncols - 1) / ncols;
1452 }
1453 int widths[] = new int[ncols+1];
1454 int heights[] = new int[nrows+1];
1455 int x = 0;
1456 int y = 0;
1457 for (int i = 0 ; i < ncomponents ; i++) {
1458 Component comp = parent.getComponent(i);
1459 Dimension d = (min
1460 ? comp.getMinimumSize()
1461 : comp.getPreferredSize());
1462 if (widths[x] < d.width) {
1463 widths[x] = d.width;
1464 }
1465 if (heights[y] < d.height) {
1466 heights[y] = d.height;
1467 }
1468 x++;
1469 if (x >= ncols) {
1470 x = 0;
1471 y++;
1472 }
1473 }
1474 for (int i = 0; i < ncols; i++) {
1475 widths[ncols] += widths[i];
1476 }
1477 for (int i = 0; i < nrows; i++) {
1478 heights[nrows] += heights[i];
1479 }
1480 return new int[][] { widths, heights };
1481 }
1482
1483 public Dimension getSize(Container parent, boolean min) {
1484 int sizes[][] = getGridSizes(parent, min);
1485 int widths[] = sizes[0];
1486 int heights[] = sizes[1];
1487 int nrows = heights.length-1;
1488 int ncols = widths.length-1;
1489 int w = widths[ncols];
1490 int h = heights[nrows];
1491 Insets insets = parent.getInsets();
1492 return new Dimension(insets.left+insets.right + w+(ncols+1)*hgap,
1493 insets.top+insets.bottom + h+(nrows+1)*vgap);
1494 }
1495
1496 public Dimension preferredLayoutSize(Container parent) {
1497 return getSize(parent, false);
1498 }
1499
1500 public Dimension minimumLayoutSize(Container parent) {
1501 return getSize(parent, true);
1502 }
1503
1504 public void layoutContainer(Container parent) {
1505 int pref[][] = getGridSizes(parent, false);
1506 int min[][] = getGridSizes(parent, true);
1507 int minwidths[] = min[0];
1508 int minheights[] = min[1];
1509 int prefwidths[] = pref[0];
1510 int prefheights[] = pref[1];
1511 int nrows = minheights.length - 1;
1512 int ncols = minwidths.length - 1;
1513 Insets insets = parent.getInsets();
1514 int w = parent.getWidth() - insets.left - insets.right;
1515 int h = parent.getHeight() - insets.top - insets.bottom;
1516 w = w - (ncols+1)*hgap;
1517 h = h - (nrows+1)*vgap;
1518 int widths[] = calculateSizes(w, ncols, minwidths, prefwidths);
1519 int heights[] = calculateSizes(h, nrows, minheights, prefheights);
1520 int ncomponents = parent.getComponentCount();
1521 int x = insets.left + hgap;
1522 int y = insets.top + vgap;
1523 int r = 0;
1524 int c = 0;
1525 for (int i = 0; i < ncomponents; i++) {
1526 parent.getComponent(i).setBounds(x, y, widths[c], heights[r]);
1527 x += widths[c++] + hgap;
1528 if (c >= ncols) {
1529 c = 0;
1530 x = insets.left + hgap;
1531 y += heights[r++] + vgap;
1532 if (r >= nrows) {
1533
1534 break;
1535 }
1536 }
1537 }
1538 }
1539
1540 public static int[] calculateSizes(int total, int num,
1541 int minsizes[], int prefsizes[])
1542 {
1543 if (total <= minsizes[num]) {
1544 return minsizes;
1545 }
1546 if (total >= prefsizes[num]) {
1547 return prefsizes;
1548 }
1549 int sizes[] = new int[total];
1550 int prevhappy = 0;
1551 int nhappy = 0;
1552 int happysize = 0;
1553 do {
1554 int addsize = (total - happysize) / (num - nhappy);
1555 happysize = 0;
1556 for (int i = 0; i < num; i++) {
1557 if (sizes[i] >= prefsizes[i] ||
1558 minsizes[i] + addsize > prefsizes[i])
1559 {
1560 happysize += (sizes[i] = prefsizes[i]);
1561 nhappy++;
1562 } else {
1563 sizes[i] = minsizes[i] + addsize;
1564 }
1565 }
1566 } while (nhappy < num && nhappy > prevhappy);
1567 return sizes;
1568 }
1569 }
1570
1571 public static class ImageCanvas extends Canvas {
1572 BufferedImage image;
1573
1574 public void setImage(BufferedImage img) {
1575 this.image = img;
1576 }
1577
1578 public BufferedImage getImage() {
1579 return image;
1580 }
1581
1582 public void checkImage(int w, int h) {
1583 if (image == null ||
1584 image.getWidth() < w ||
1585 image.getHeight() < h)
1586 {
1587 image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
1588 }
1589 }
1590
1591 public void setReference(BufferedImage img) {
1592 checkImage(img.getWidth(), img.getHeight());
1593 Graphics g = image.createGraphics();
1594 g.drawImage(img, 0, 0, null);
1595 g.setColor(Color.white);
1596 g.fillRect(0, 0, 30, 10);
1597 g.fillRect(30, 0, 10, 30);
1598 g.fillRect(10, 30, 30, 10);
1599 g.fillRect(0, 10, 10, 30);
1600 g.dispose();
1601 }
1602
1603 public void setDiff(BufferedImage imgref, BufferedImage imgtst) {
1604 int w = Math.max(imgref.getWidth(), imgtst.getWidth());
1605 int h = Math.max(imgref.getHeight(), imgtst.getHeight());
1606 checkImage(w, h);
1607 Graphics g = image.createGraphics();
1608 g.drawImage(imgref, 0, 0, null);
1609 g.setXORMode(Color.white);
1610 g.drawImage(imgtst, 0, 0, null);
1611 g.setPaintMode();
1612 g.setColor(new Color(1f, 1f, 0f, 0.25f));
1613 g.fillRect(10, 10, 20, 20);
1614 g.setColor(new Color(1f, 0f, 0f, 0.25f));
1615 g.fillRect(0, 0, 30, 10);
1616 g.fillRect(30, 0, 10, 30);
1617 g.fillRect(10, 30, 30, 10);
1618 g.fillRect(0, 10, 10, 30);
1619 g.dispose();
1620 }
1621
1622 public Dimension getPreferredSize() {
1623 if (image == null) {
1624 return new Dimension();
1625 } else {
1626 return new Dimension(image.getWidth(), image.getHeight());
1627 }
1628 }
1629
1630 public void paint(Graphics g) {
1631 g.drawImage(image, 0, 0, null);
1632 }
1633 }
1634 }